﻿namespace UnityEditor.Controls.ProjectControl.ViewModel
{
    using System;
    using System.Collections.Specialized;
    using System.IO;
    using UnityEditor.IO;
    using UnityEngine.IO;

    /// <summary>
    /// One item in the treeview of of items
    /// </summary>
    public class ProjectItem : NotifiableObject
    {
        #region Private fields
        private static ProjectItemFilter treeItemFilter;

        private TreeItemList children;

        private string fullPath;

        private bool isExpanded;

        private bool isSelected;

        private string name;

        private ProjectItem parent;

        #endregion

        #region Constructor
        /// <summary>
        /// Standard constructor from itemName and link to parent
        /// </summary>
        /// <param name="itemName"></param>
        /// <param name="parent"></param>
        public ProjectItem(string fullPath, ProjectItem parent, uint tryLoadDepth = 0)
        {
            if (fullPath == null || fullPath.Length == 0)
                throw new ArgumentException();

            this.FullPath = fullPath;
            this.Name = Path.GetFileName(FullPath);
            this.IsFullyLoaded = false;
            this.Parent = parent;
            this.Children = new TreeItemList();
            this.Children.Clear();

            if (this.IsFolder == false)
            {
                this.IsFullyLoaded = true;
            }
            else
            {
                // Check whether the item is valid
                children.CollectionChanged += new NotifyCollectionChangedEventHandler(
                    (o, e) =>
                    {
                        if (e == null || e.NewItems == null)
                            return;

                        foreach (ProjectItem item in e.NewItems)
                        {
                            if (ProjectItem.Filter.IsValidTreeItem(item) == false)
                            {
                                throw new ArgumentException("Tree item is not valid.");
                            }
                        }
                    });

                this.TryLoadChildren(tryLoadDepth);
            }
        }

        public TreeItemList Children
        {
            get
            {
                return this.children;
            }
            set
            {
                this.children = value;
                this.NotifyPropertyChanged(() => this.Children);
            }
        }

        public string FullPath
        {
            get
            {
                return this.fullPath;
            }
            set
            {
                this.fullPath = value;
            }
        }

        public string RelativePath
        {
            get
            {
                string ret = this.FullPath;
                if (this.FullPath.Contains(IOManager.Instance.AssetsFolder))
                {
                    ret = ret.Remove(0, IOManager.Instance.AssetsFolder.Length + 1);
                }
                else
                    throw new Exception("Invalid full path");
                return ret;
            }
            set
            {
                this.FullPath = IOManager.Instance.AssetsFolder + "\\" + value;
            }
        }

        public bool IsExpanded
        {
            get
            {
                return isExpanded;
            }
            set
            {
                isExpanded = value;
                this.NotifyPropertyChanged(() => this.IsExpanded);
            }
        }

        public bool IsFolder
        {
            get
            {
                return ((File.GetAttributes(this.FullPath) & FileAttributes.Directory) == FileAttributes.Directory);
            }
        }

        public bool IsFullyLoaded { get; set; }

        public bool IsSelected
        {
            get
            {
                return isSelected;
            }
            set
            {
                isSelected = value;
                this.NotifyPropertyChanged(() => this.IsSelected);
            }
        }

        public string Name
        {
            get
            {
                return this.name;
            }

            set
            {
                this.name = value;
                this.NotifyPropertyChanged(() => this.Name);
            }
        }

        public ProjectItem Parent
        {
            get
            {
                return this.parent;
            }

            set
            {
                this.parent = value;
                this.NotifyPropertyChanged(() => this.Parent);
            }
        }

        #endregion Constructor

        #region Properties
        internal static ProjectItemFilter Filter
        {
            get
            {
                if (treeItemFilter == null)
                    treeItemFilter = new ProjectItemFilter();
                return treeItemFilter;
            }
        }

        #endregion

        #region Methods
        public void MoveFrom(ProjectItem item)
        {
            if (item.IsFolder)
                Directory.Move(item.FullPath, this.FullPath);
            else
                File.Move(item.FullPath, Path.Combine(this.FullPath, Path.GetFileName(item.FullPath)));

            if (item.Parent != null)
                item.Parent.Children.Remove(item);
            item.Parent = this;
            item.FullPath = Path.Combine(this.FullPath, Path.GetFileName(item.FullPath));
            this.Children.Add(item);
        }

        public bool IsAncestorOf(ProjectItem child)
        {
            foreach (ProjectItem c in this.Children)
                if (c == child || c.IsAncestorOf(child))
                    return true;
            return false;
        }

        public void TryLoadChildren(uint tryLoadDepth = 1)
        {
            if (tryLoadDepth == 0)
                return;

            uint nextLoadDepth = (uint)tryLoadDepth - 1 >= 0 ? tryLoadDepth - 1 : 0;

            if (this.IsFullyLoaded == false && tryLoadDepth >= 1)
            {
                if (this.IsFolder)
                {
                    this.Children.Clear();

                    string path = this.FullPath;

                    string[] directoryNames = Directory.GetDirectories(path);
                    foreach (var directoryName in directoryNames)
                    {
                        var item = new ProjectItem(directoryName, this, nextLoadDepth);
                        this.Children.Add(item);
                    }

                    string[] filePaths = Directory.GetFiles(path);
                    foreach (var fileName in filePaths)
                    {
                        var item = new ProjectItem(fileName, this, nextLoadDepth);
                        this.Children.Add(item);
                    }

                }

                this.IsFullyLoaded = true;
            }

            foreach (ProjectItem child in this.Children)
                child.TryLoadChildren(nextLoadDepth);
        }

        public void TryToDeleteSelf()
        {
            if (this.IsFolder)
            {
                this.DeleteDirectory(this.FullPath);

                this.Parent.Children.Remove(this);
                this.Children.Clear();
            }
            else
            {
                FileInfo fileInfo = new FileInfo(this.FullPath);
                fileInfo.Attributes = FileAttributes.Normal;
                File.Delete(this.FullPath);
            }
        }

        private void DeleteDirectory(string path)
        {
            // invalid files
            string[] files = Directory.GetFiles(path);
            foreach (string fileName in files)
            {
                File.SetAttributes(fileName, FileAttributes.Normal);
                File.Delete(fileName);
            }

            // invalid directory
            string[] directories = Directory.GetDirectories(path);
            foreach (string folder in directories)
            {
                this.DeleteDirectory(folder);
            }

            Directory.Delete(path, false);
            // DirectoryInfo directoryInfo = new DirectoryInfo(path);
            //directoryInfo.Attributes = FileAttributes.Normal;
            // directoryInfo.Delete(true);
        }

        #endregion
    }
}
